package com.sad.jetpack.v1.datamodel.api;


import java.util.ArrayList;
import java.util.List;

public class DataModelProducerImpl<RP> implements IDataModelProducer<RP> {

    private IDataModelRequest request;
    private IDataModelObtainedCallback<RP> callback;
    private IDataModelObtainedExceptionListener exceptionListener;
    private IDataModelProductEngine<RP> engine;
    private List<IDataModelInterceptorInput<RP>> interceptorInputs=new ArrayList<>();
    private List<IDataModelInterceptorOutput<RP>> interceptorOutputs=new ArrayList<>();

    private DataModelProducerImpl(){

    }

    public static <RP> IDataModelProducer<RP> newInstance(){
        return new DataModelProducerImpl<RP>();
    }

    @Override
    public IDataModelProducer<RP> newRequest(IDataModelRequest request) {
        this.request=request;
        return this;
    }

    @Override
    public IDataModelProducer<RP> addInputInterceptor(IDataModelInterceptorInput<RP> input, int index) {
        this.interceptorInputs.add(index,input);
        return this;
    }

    @Override
    public IDataModelProducer<RP> removeInputInterceptor(int index) {
        this.interceptorInputs.remove(index);
        return this;
    }

    @Override
    public IDataModelProducer<RP> removeInputInterceptor(String tag) {
        List<IDataModelInterceptorInput<RP>> cloneCurrInterceptorInput=new ArrayList<>();
        for (IDataModelInterceptorInput<RP> interceptorInput:this.interceptorInputs){
            if (!interceptorInput.tag().equals(tag)){
                cloneCurrInterceptorInput.add(interceptorInput);
            }
        }
        this.interceptorInputs=cloneCurrInterceptorInput;
        return this;
    }

    @Override
    public IDataModelProducer<RP> clearInputInterceptor() {
        this.interceptorInputs.clear();
        return this;
    }

    @Override
    public IDataModelProducer<RP> addInputInterceptor(IDataModelInterceptorInput input) {
        this.interceptorInputs.add(input);
        return this;
    }

    @Override
    public IDataModelProducer<RP> addOutputInterceptor(IDataModelInterceptorOutput output) {
        interceptorOutputs.add(output);
        return this;
    }

    @Override
    public IDataModelProducer<RP> callback(IDataModelObtainedCallback callback) {
        this.callback=callback;
        return this;
    }

    @Override
    public IDataModelProducer<RP> exceptionListener(IDataModelObtainedExceptionListener exceptionListener) {
        this.exceptionListener=exceptionListener;
        return this;
    }

    @Override
    public IDataModelProducer<RP> engine(IDataModelProductEngine<RP> engine) {
        this.engine=engine;
        return this;
    }
    private IDataModelProduceFuture<RP> produceFuture;
    @Override
    public IDataModelProduceFuture<RP> execute() {
        produceFuture= DataModelProduceFutureProvider.<RP>get(request.tag());
        if (produceFuture==null){
            produceFuture=new InternalDataModelProduceFuture<>(request,this);
        }
        else {
            produceFuture.toCreator().updateRequest(request);
        }
        produceFuture.toCreator().updateBindingProducer(this);
        produceFuture.registerSelf();
        produceFuture.notifyObserversNotStarted(request);
        addInputInterceptor(new InternalTerminalDataModelInterceptorInput<RP>(interceptorOutputs,engine));
        InternalDataModelChainInput<RP> chainInput=new InternalDataModelChainInput<>(interceptorInputs,produceFuture, callback, new IDataModelObtainedExceptionListener() {
            @Override
            public void onDataObtainedException(IDataModelRequest request, Throwable throwable) {
                produceFuture.notifyObserversException(request,throwable);
                if (exceptionListener!=null){
                    exceptionListener.onDataObtainedException(request,throwable);
                }
            }
        });
        produceFuture.notifyObserversProcessing(request);
        chainInput.proceed(request,null,chainInput.currIndex());
        return produceFuture;
    }

    @Override
    public IDataModelProducer<RP> interceptorOutputs(List<IDataModelInterceptorOutput<RP>> list) {
        this.interceptorOutputs=list;
        return this;
    }

    @Override
    public IDataModelProducer<RP> resetInterceptorInputs(IDataModelInterceptorInputResetting<RP> inputResetting) {
        if (inputResetting!=null){
            this.interceptorInputs=inputResetting.onInterceptorInputReset(this.interceptorInputs);
        }
        return this;
    }

    @Override
    public IDataModelProducer<RP> resetInterceptorOutputs(IDataModelInterceptorOutputResetting<RP> outputResetting) {
        if (outputResetting!=null){
            this.interceptorOutputs=outputResetting.onInterceptorOutputReset(this.interceptorOutputs);
        }
        return this;
    }

    @Override
    public IDataModelProducer<RP> interceptorInputs(List<IDataModelInterceptorInput<RP>> list) {
        this.interceptorInputs=list;
        return this;
    }

    @Override
    public IDataModelProducer<RP> addOutputInterceptor(IDataModelInterceptorOutput<RP> output, int index) {
        this.interceptorOutputs.add(index,output);
        return this;
    }

    @Override
    public IDataModelProducer<RP> removeOutputInterceptor(int index) {
        this.interceptorOutputs.remove(index);
        return this;
    }

    @Override
    public IDataModelProducer<RP> removeOutputInterceptor(String tag) {
        List<IDataModelInterceptorOutput<RP>> cloneCurrInterceptorOutput=new ArrayList<>();
        for (IDataModelInterceptorOutput<RP> interceptorOutput:this.interceptorOutputs){
            if (!interceptorOutput.tag().equals(tag)){
                cloneCurrInterceptorOutput.add(interceptorOutput);
            }
        }
        this.interceptorOutputs=cloneCurrInterceptorOutput;
        return this;
    }

    @Override
    public IDataModelProducer<RP> clearOutputInterceptor() {
        this.interceptorOutputs.clear();
        return this;
    }



    /*@Override
    public INetDataProcessorMaster request(INetDataRequest request) {
        this.request=request;
        return this;
    }

    @Override
    public INetDataProcessorMaster callback(INetDataObtainedCallback callback) {
        this.callback=callback;
        return this;
    }

    @Override
    public INetDataProcessorMaster engine(IEngine engine) {
        this.engine=engine;
        return this;
    }

    @Override
    public void execute() {
        if (this.engine==null){
            this.engine=new JsoupNetDataEngine() {
                @Override
                public void onResetJsoupConnection(INetDataRequest request, Connection connection) {

                }
            };
        }
        this.engine.onDoExecute(this);
    }

    @Override
    public INetDataRequest request() {
        return this.request;
    }

    @Override
    public INetDataObtainedCallback callback() {
        return this.callback;
    }

    public IEngine engine() {
        return this.engine;
    }*/
}
